package com.dly.blog.plugin;

import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.dly.blog.domain.File;
import com.dly.blog.domain.dto.BucketPolicyConfigDTO;
import com.dly.blog.enums.FileState;
import com.dly.blog.enums.UploadType;
import com.dly.blog.exception.FileExceptionEnum;
import com.dly.blog.properties.UploadProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.minio.*;
import io.minio.http.Method;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;

/**
 * @Author: DaiLiYu
 * @Date: 2023/7/12 22:17
 */
@Component
@Slf4j
@ConditionalOnBean(MinioClient.class)
@AllArgsConstructor
public class MinioUploadFile implements UploadFilePlugin {

    private final MinioClient minioClient;

    private final UploadProperties uploadProperties;

    private final ObjectMapper objectMapper;

    private final Sequence sequence = new Sequence(null);

    @Override
    public File uploadFile(File fileInfo, String filePath, MultipartFile multipartFile) {

        log.info("into 文件上传中 ......");

        try {

            String bucket = uploadProperties.getMinio().getBuketName();
            //查询桶位是否存在
            boolean found =
                    minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());

            //不存在就去创建一个
            if (!found) {
                //创建存储桶并设置只读权限
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
                BucketPolicyConfigDTO bucketPolicyConfigDto = createBucketPolicyConfigDto(bucket);
                SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(objectMapper.writeValueAsString(bucketPolicyConfigDto))
                        .build();
                minioClient.setBucketPolicy(setBucketPolicyArgs);
            }

            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(filePath + multipartFile.getOriginalFilename())
                            .stream(multipartFile.getInputStream(), multipartFile.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE)
                            .contentType(multipartFile.getContentType())
                            .build());

            fileInfo.setFileState(FileState.EFFECTIVE);
            fileInfo.setFilePath(filePath);
            fileInfo.setId(String.valueOf(sequence.nextId()));
            fileInfo.setUrl(uploadProperties.getMinio().getEndpoint() + StringPool.SLASH + bucket + filePath + multipartFile.getOriginalFilename());
        }catch (Exception e){
            log.debug("into 文件上传失败 {}", e.getMessage());
            throw FileExceptionEnum.FILE_UPLOAD_ERROR.getFileException();
        }

        log.info("into 文件成功 ......");

        return fileInfo;
    }



    @Override
    public File uploadFileTerm(File fileInfo, String filePath, MultipartFile multipartFile) {

        log.info("into 文件上传中 ......");

        try {

            //获取私有的存储库
            String bucket = uploadProperties.getMinio().getPrivateBuketName();

            //查询桶位是否存在
            boolean found =
                    minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());

            //不存在就去创建一个
            if (!found) {
                //创建存储桶并设置只读权限
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
                BucketPolicyConfigDTO bucketPolicyConfigDto = createPrivateBucketPolicyConfigDto(bucket);
                SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(objectMapper.writeValueAsString(bucketPolicyConfigDto))
                        .build();
                minioClient.setBucketPolicy(setBucketPolicyArgs);
            }

            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(filePath + multipartFile.getOriginalFilename())
                            .stream(multipartFile.getInputStream(), multipartFile.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE)
                            .contentType(multipartFile.getContentType())
                            .build());

            //获取预览的url
            String presignedUrl = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .bucket(bucket)
                            .object(filePath + multipartFile.getOriginalFilename())
                            .expiry(fileInfo.getExpirationDate())
                            .method(Method.GET)
                            .build()
            );

            fileInfo.setFileState(FileState.EFFECTIVE);
            fileInfo.setFilePath(filePath);
            fileInfo.setUrl(presignedUrl);
            fileInfo.setId(String.valueOf(sequence.nextId()));
        }catch (Exception e){
            log.debug("into 文件上传失败 {}", e.getMessage());
            throw FileExceptionEnum.FILE_UPLOAD_ERROR.getFileException();
        }

        log.info("into 文件成功 ......");

        return fileInfo;

    }

    /**
     * 创建存储桶的访问策略，设置为只读权限
     */
    private BucketPolicyConfigDTO createPrivateBucketPolicyConfigDto(String bucketName) {
        return BucketPolicyConfigDTO.builder()
                .Version("2012-10-17")
                .Statement(List.of())
                .build();
    }

    /**
     * 创建存储桶的访问策略，设置为只读权限
     */
    private BucketPolicyConfigDTO createBucketPolicyConfigDto(String bucketName) {
//        设置bucket 可以遍历
//        BucketPolicyConfigDTO.Statement statement1 = BucketPolicyConfigDTO.Statement.builder()
//                .Effect("Allow")
//                .Principal("*")
//                .Action(List.of("s3:GetBucketLocation", "s3:ListBucket"))
//                .Resource("arn:aws:s3:::"+bucketName).build();

        BucketPolicyConfigDTO.Statement statement = BucketPolicyConfigDTO.Statement.builder()
                .Effect("Allow")
                .Principal("*")
                .Action("s3:GetObject")
                .Resource("arn:aws:s3:::"+bucketName+"/**").build();

        return BucketPolicyConfigDTO.builder()
                .Version("2012-10-17")
                .Statement(List.of(statement))
                .build();
    }

    @Override
    public boolean supports(UploadType delimiter) {
        return UploadType.MINIO.equals(delimiter);
    }
}
